#include "main.h"

int sockArr[3000];                // 保存套接字的数组
struct sockaddr_in addrArr[3000]; // 保存地址的数组
int count = 0;                    // 当前有多少客户端
int epollfd = -1;                 // epoll对象
int fd = -1;                      // 监听套接字

// 把字符串IP转为数字IP

uint32_t stringIPtoNum(char *pIP)
{
     uint32_t nIP = 0;
     // AF_INET是代表IPV4地址的意思，AF_INET6代表IPV6
     inet_pton(AF_INET, pIP, &nIP);
     return nIP;
}

// 把数字IP转为字符串IP
void numIPtoString(uint32_t ip, char *psIP, int size)
{
     inet_ntop(AF_INET, &ip, psIP, size);
}

//参数1：接收消息用的套接字
//参数2：接收到消息结构体
//这两个参数都是read函数接受用到的变量
// void work(int fd,MSG m1)
// {
//      printf("%s\n", m1.buf);
//      // 遍历所有已经连接的用户
//      for (int j = 0; j < count; j++)
//      {
//           // 判断地址数组中的地址是否和需要发送消息的地址相同
//           if (addrArr[j].sin_addr.s_addr == m1.ip && addrArr[j].sin_port == m1.port)
//           {
//                send(sockArr[j], &m1, sizeof(m1), 0);
//           }
//      }
// }

// int main()
// {
//      openlog("my_program", LOG_PID | LOG_CONS, LOG_LOCAL1);
//      int num = 10;
//      syslog(LOG_SYSLOG|LOG_DEBUG, "变量 num 的值为 %d", num);

//      // 模拟错误情况
//      if (num > 5)
//      {
//           syslog(LOG_DEBUG, "变量 num 大于 5，可能存在问题");
//      }

//      // 关闭系统日志
//      closelog();

int initServer() // 初始化服务器，里面主要是创建监听套接字和创建EPOLL对象
{
     // 创建一个SOCKET，并且返回一个套接字的文件描述符
     fd = socket(AF_INET, SOCK_STREAM, 0);
     if (fd == -1)
     {
          perror("");
     }

     int val = 1; // 1代表打开地址重复利用功能，0代表关闭
     // SOL_SOCKET是通用socket设置的意思
     // SO_REUSEADDR是地址重复利用的意思
     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

     // 地址结构体，需要填写IP，端口，地址族（IPV4/IPV6）
     //  typedef struct sockaddr_in MYADDR;
     struct sockaddr_in addr;
     addr.sin_addr.s_addr = stringIPtoNum("192.168.70.37"); // 指定IP
     addr.sin_port = htons(9001);                           // 指定端口
     addr.sin_family = AF_INET;                             // 指定地址协议族

     // 第二个参数因为历史原因，系统设定的是struct sockaddr类型，而现在常用的类型是struct sockaddr_in
     // 所以这里要做一个强制转换
     if (-1 == bind(fd, (struct sockaddr *)&addr, sizeof(addr)))
     {
          perror("");
     }
     // 启动监听功能，允许5个客户端排队等待
     if (-1 == listen(fd, 5))
     {
          perror("");
     }

     // 创建一个EPOLL对象
     epollfd = epoll_create1(0);
     if (epollfd < 0)
     {
          perror("epoll_create1");
          return 1;
     }

     // 事件结构体里面主要填写两个变量，一个是感兴趣额的事件，另一个是套接字
     struct epoll_event ev; // 定义了一个事件结构体变量
     ev.data.fd = fd;       // 把监听套接字设置到事件结构体中
     ev.events = EPOLLIN;   // 把套接字感兴趣的事件设置为EPOLLIN（输入/可读事件）
     // 控制epoll对象，把套接字和它对应的结构体加入到epoll对象中
     if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0)
     {
          perror("epoll_ctl");
          close(fd);
     }
}



void run()
{
     struct epoll_event evArr[MAX_FDS];
     while (1)
     {
          // 等待在epoll对象中的套接字发生你填写在结构体中指定的事件发生，如果发生了就会把所有发生事件的套接字对应的结构体存入第二个参数（数组）中
          // 返回值为发生感兴趣的事件的套接字数量
          int ret = epoll_wait(epollfd, evArr, MAX_FDS, -1);
          if (ret < 0)
          {
               perror("epoll_wait");
          }
          else if (ret > 0)
          {
               for (int i = 0; i < ret; i++)
               {
                    if (evArr[i].data.fd == fd) // 判断结构体中的套接字是否是监听套接字
                    {
                         struct sockaddr_in clientAddr; // 用来存放客户端的地址(来电显示)
                         socklen_t len = sizeof(clientAddr);
                         // 阻塞等待客户端的链接，如果有客户链接上该函数才会返回，否则会一个等待
                         int newSocketFd = accept(fd, (struct sockaddr *)&clientAddr, &len);
                         if (newSocketFd == -1)
                         {
                              perror("");
                         }

                         addrArr[count] = clientAddr;
                         sockArr[count] = newSocketFd;
                         count++;
                         // 事件结构体里面主要填写两个变量，一个是感兴趣额的事件，另一个是套接字
                         struct epoll_event ev;    // 定义了一个事件结构体变量
                         ev.data.fd = newSocketFd; // 把监听套接字设置到事件结构体中
                         ev.events = EPOLLIN;      // 把套接字感兴趣的事件设置为EPOLLIN（输入/可读事件）
                         if (epoll_ctl(epollfd, EPOLL_CTL_ADD, newSocketFd, &ev) < 0)
                         {
                              perror("epoll_ctl");
                              close(fd);
                         }

                         char szIP[16]; // 把手的数字IP转诶字符串IP，用来打印
                         numIPtoString(clientAddr.sin_addr.s_addr, szIP, sizeof(szIP));

                         printf("又添加了一个套接字和结构体到epoll对象中 %s %d\n", szIP, ntohs(clientAddr.sin_port));
                         // printf("接受了新的链接，并且产生了新的套接字，所以最大的文件描述符也要更新一下，值为:%d\n",newSocketFd);
                    }
                    else // 通信套接字
                    {
                         MSG m1;
                         int ret = recv(evArr[i].data.fd, &m1, sizeof(m1),0); // 既然套接字有可读事件，那么就读取套接字上的内容
                         if (ret <= 0)                                      // 如果接收数据出错了，就把套接字关闭，并且从集合中删除掉
                         {
                              close(evArr[i].data.fd);
                              // 事件结构体里面主要填写两个变量，一个是感兴趣额的事件，另一个是套接字
                              struct epoll_event ev;         // 定义了一个事件结构体变量
                              ev.data.fd = evArr[i].data.fd; // 把监听套接字设置到事件结构体中
                              ev.events = EPOLLIN;           // 把套接字感兴趣的事件设置为EPOLLIN（输入/可读事件）
                              // 控制epoll对象，从里面删除出错的套接字和结构体
                              if (epoll_ctl(epollfd, EPOLL_CTL_DEL, evArr[i].data.fd, &ev) < 0)
                              {
                                   perror("epoll_ctl");
                              }

                              // 在这里还需要在地址数组和套接字数组中删除对应的内容
                              // 在这里还需要在地址数组和套接字数组中删除对应的内容
                              // 在这里还需要在地址数组和套接字数组中删除对应的内容

                              printf("%d号套接字对应的客户端退出了\n", ev.data.fd);
                         }
                         else
                         {
                              // 所有的业务逻辑在此编写，包所有的和业务相关的代码转给work函数去完成，这里只有底层基本网络代码
                              // work(evArr[i].data.fd,m1);
                              work1(evArr[i].data.fd,m1);
                         }
                    }
               }
          }
     }
}

// #include <stdio.h>
// #include <stdlib.h>
// #include <aio.h>
// #include <unistd.h>
// #include <string.h>
// #include <fcntl.h>

// #define BUFFER_SIZE 1024

// int main() {
//     int fd;
//     struct aiocb my_aiocb;
//     char buffer[BUFFER_SIZE];

//     // 打开文件
//     fd = open("example.txt", O_RDONLY);
//     if (fd == -1) {
//         perror("open");
//         exit(1);
//     }

//     // 初始化 aiocb 结构体
//     memset(&my_aiocb, 0, sizeof(struct aiocb));
//     my_aiocb.aio_fildes = fd;
//     my_aiocb.aio_buf = buffer;
//     my_aiocb.aio_nbytes = BUFFER_SIZE;
//     my_aiocb.aio_offset = 0;

//     // 发起异步读取操作
//     if (aio_read(&my_aiocb) == -1) {
//         perror("aio_read");
//         close(fd);
//         exit(1);
//     }

//     // 可以在等待操作完成的同时进行其他工作
//     printf("Doing other work while waiting for the read to complete...\n");
//     sleep(1);  // 模拟其他工作

//     // 检查操作是否完成
//     while (aio_error(&my_aiocb) == EINPROGRESS) {
//         printf("Still waiting...\n");
//         sleep(1);
//     }

//     // 获取操作结果
//     int ret = aio_return(&my_aiocb);
//     if (ret == -1) {
//         perror("aio_return");
//         close(fd);
//         exit(1);
//     } else {
//         // 输出读取的数据
//         printf("Read %d bytes: %s\n", ret, buffer);
//     }

//     // 关闭文件
//     close(fd);

//     return 0;
// }